[React] よーし! いっちょReactやってみっか! #3 基本キーワード編
はじめに
CX事業本部の中安です。まいどです。
モバイルアプリエンジニアな自分がReact
を始めてみることにした
「よーし! いっちょReactやってみっか!」シリーズの続きです。
今回もよろしくお願いします。
前回は作成されたプロジェクトの中身をサラッと見ていきました。
今回は、React
でよく使われる基本キーワードを抑えていくことにします。
React
独自なものやJavaScript
のものが混ざっていますが、
頭が混乱しないように言葉の定義を覚えておくのも大事ですね。
React
初回のブログでも「React
とは」については書きましたが、あらためてこちらでもまとめようと思います。
React
React
はFacebook社によって開発されたJavaScript
のためのユーザインターフェイス構築用のライブラリで、
Vue
やAngular
などのWEBフロント用ライブラリに並ぶ人気のライブラリです。
他のライブラリとの比較はこちらの記事が参考になるでしょう。
React
はプロジェクト化せずとも1つのHTMLファイルだけに取り入れることも可能ですし、他のライブラリとの共存も可能とされています。
プロジェクト化したとしても書き方に制約がかかることが少ないという意味では
「フレームワーク」ではなく「ライブラリ」という位置づけのほうが正しそうです。
以下はReact
公式が掲げるReact
の特長です。
宣言的ビュー
表示の構造をプログラマブルに書くことができ、 データ表示の配置場所を定義しておくことで、データが変更されたら同時に表示も変更されるという動きをすることを指します。
「表示の構造をプログラマブルに書く」とは、React
の場合はHTML
ライクな書き方「JSX
」のことをいいます。
JSX
については後述します。
コンポーネントベース
複雑なUIであっても、それらを「部品 = コンポーネント」に分けることで煩雑なソースコードにはならないことを指します。
React
は変更したい部分だけを修正できる特長があり、ソースコードの管理や再利用がしやすいとされています。
コンポーネントについては後述します。
Learn Once, Write Anywhere
「一度学習すれば、どこでも使える」という意味ですが、これはReact
がWEBフロントサイドの技術だけではなく、
モバイルアプリやサーバサイドレンダリングなどでも利用することができることを指しています。
React.js
React
は、そもそもJavaScript
でUIを構築するライブラリとして生まれた経緯からReact.js
とも呼ばれます。
しかしながら "Learn Once, Write Anywhere" の元、
モバイルアプリ開発用のReact
派生であるReact Native
であったり、
JavaScript
ではなくTypeScript
を使用するReact+TypeScript
構成であったり、
React
という言葉自体が2010年代中盤からは狭義から広義な意味へと変わっています。
このブログもシリーズが続けば、React Native
やReact+TypeScript
なども手を出していきたいところですが、
まずはReact.js
ベースでやっていこうと思います。
DOMと仮想DOM
DOM
DOM
はDocument Object Model
の略で、
JavaScript
からHTML
やXML
を操作するための「プログラミングインターフェイス」とされています。
WEBページであれば画面に表示されているHTML
の要素をひとつのモデルオブジェクトとして扱い、中の構造や内容を変更することが出来ます。
[まとめ]
DOM = JavaScript
からHTML
やXML
を操作するための「プログラミングインターフェイス」
仮想DOM
DOM
の操作は、ツリー構築から始まりレイアウト計算・描画というプロセスを経るらしく、
この処理をWEBブラウザが受け持っているために描画コストがかかってしまうとのことです。
React
では、これらをJavaScript
側で受け持たせることで描画コストを下げる仕組みを使います。
この仕組みを「仮想DOM」(Virtual DOM
)と呼びます。
仮想DOMを用いることで、必要最低限の差分を本来のDOMに反映するので、描画コストが向上するとのことです。
[まとめ]
仮想DOM = JavaScript
でDOM操作を処理して描画コストを下げる仕組み
ノードとエレメント
DOM
を扱うにあたって「ノード」と「エレメント」という言葉がよく出てきます。
今一度整理してみましょう。
ノード
ざっくりいうと、DOM
の中を構成するモノひとつひとつのことを総じてノードといいます。
エレメント
ざっくいうと、ノードの中でもHTML(またはXML)タグ自体のことをエレメントといいます。 日本語では「要素ノード」ともいいますが、要はノードの一部ということですね。
たとえば
<div id="root"><a href="http://hoge">リンク</a></div>
この場合、id=root
の<div>
の中には、<a>
というエレメントが1つ存在しています。
ノードはどれだけ存在しているかというと、<a>
という要素ノードと、href
という属性ノード、"リンク"
というテキストノードが存在しています。
このあたり細かい話は以下の記事を参考ください。
JSXとBabel
JSX
React
ではJSX
という仕組みをよく使うことになります。
JSX
はJavaScript
でXML
のような構文記述ができるようになる言語拡張と説明されています。
簡単に言うとJSX
でHTML
を埋め込むような書き方ができるという仕組みです。
間違いたくないのは「HTML
を直接書いてるのではなく、あくまでHTML
ライクに書ける」ということでしょうか。
let element = ( <div> <h1>Hello World</h1> </div> );
単純なJavaScript
であればHTML
を文字列で変数に代入するところですが、JSX
はそういうことをしていないことが分かると思います。
また、値の全体をカッコで囲っていますが、これはあってもなくてもいいとのことです。
ここでは分かりやすくするためにカッコを付けています。
JSX
についてはこちらのドキュメントも参考ください。
[まとめ]
JSX = HTML
ライクな記述ができるJavaScript
の言語拡張
Babel
Babel
は、前述のJSX
をJavaScript
のソースコード上で使用可能にするためのトランスコンパイラです。
これがないとJSX
は動作せずエラーになってしまうことでしょう。
前ブログで作ったプロジェクト上のindex.js
では、特にBaebl
を意識をすることなくJSX
を利用できていますが、
これは先にライブラリ群を呼び出しているからですね。
単独のHTMLの場合は、Babel
をヘッダ等の<script>
タグ内で呼び出した上で、
実装する箇所の<script>
タグにはtype
を"text/babel"
にしてやらないと正常動作しません。
<html> <head> <!-- head部は色々と省略 --> <script src="(Babelの読み込み)"></script> </head> <body> <script type="text/babel"> let element = ( <div> <h1>Hello World</h1> </div> ); </script> </body> </html>
[まとめ]
Babel = JSX
を使用するために必要なコンパイラ
コンポーネント
React
の特長は「コンポーネントベース」のフレームワークだということを先ほども書きました。
したがって「コンポーネント」という概念は大事になってきます。
「コンポーネント」は画面表示上の部品です。
React
で作るWebアプリは、この部品を大きなものから小さなものまで組み合わせていきます。
部品一つ一つには状態やデータを持たせることができるので、 同じような表示箇所には、部品のデータだけを変えて何回も再利用することができる感じです。
たとえば、最初に作ったプロジェクトのテンプレートにあったApp.js
内の「App
」がそれにあたりますね。
関数コンポーネント
React
上の「関数コンポーネント」とは、state
を持たずレンダリング処理だけを持つコンポーネントと定義されています。
名前の通り、クラスのような形式ではなく関数のように記述します。
function Parts(props) { return ( <div> <h1>Hello World</h1> </div> ); }
また、関数コンポーネントはJavaScript
の「アロー関数」の構文を使って、もう少し簡略的に表すことができます。
上の関数コンポーネントは以下のようにも書くことができます。
const Parts = (props) => { return ( <div> <h1>Hello World</h1> </div> ); }
state
については後述します。
※関数コンポーネントはstate
を持たないと冒頭に書きましたが、
Hooks
という機能を使うことで同等のことが可能になります。これもまた後述します。
React.Component
関数コンポーネントとは違い、クラスでの記述となるコンポーネントも作ることもできます。 クラスであるゆえにプロパティやメソッド、コンストラクタなどが定義できます。
クラスがコンポーネントとして振る舞うためには React.Component
を継承してやる必要があります。
そして、その継承したクラスはrender()
というメソッドを実装する必要があり、
実際に表示するためのJSX
などを返してやらなければなりません。
class Parts(props) extends React.Component { constructor(props) { super(props); } render() { return ( <div> <h1>Hello World</h1> </div> ); } }
関数コンポーネントはstate
を持たないという性質でしたが、こちらは持つことができます。
props、state、プロパティ
ここからはコンポーネントの持たせる情報などの話になるのですが、
その前にReact
でコンポーネントはどのように生成するのかを見なくてはいけません。
先程のJSX
ではHTML
ライクにUIを宣言できると書きました。
「HTML
」ではなく「HTML
ライク」や「HTML
のように」と書いている理由は、
ここにコンポーネントを直接配置することができるからです。
たとえば、こんな感じです。
return ( <div> <MenuItem /> <MenuItem /> <MenuItem /> </div> );
ここでいうMenuItem
は、仮に自分で作ったコンポーネントです。
このようにHTML
のようにJSX
を使ってコンポーネントの配置場所を定義できるわけですね。
しかし、これが何かしらのメニューのUIであるとして、今3つ並べたMenuItem
コンポーネントはまったく同じUIとして表示されてしまうことでしょう。
それぞれのMenuItem
はそれぞれに異なった情報を渡してやり、それぞれに異なった表示をさせる必要があります。
では、どうするのかというと、ここにHTML
やXML
に与えるように属性値を与えてやればいいのです。
return ( <div> <MenuItem title="アカウント管理" /> <MenuItem title="商品管理" /> <MenuItem title="顧客管理" /> </div> );
こうすると、それぞれがメニューのアイテムになっていて、なおかつタイトルが別々のものになるということが分かります。
これを踏まえて、コンポーネント側でこの情報をどう扱うかをキーワード別に整理していきます。
props
上記の例ではtitle
が呼び出し元によって渡されました。
これをコンポーネント側で受け取って反映させる必要がありますが、
それらの渡された値(属性値)は、props
という値に入ってきます。
props
はJavaScript
のオブジェクトになっていて、先程の例では {name: "アカウント管理"}
といった感じで渡されてきます。
props
は関数コンポーネントであれば関数の引数で、クラスであればコンストラクタ、またはthis.props
で引き取ることができます。
また、このprops
は原則として読み取り専用のデータです。
[まとめ]
props
= コンポーネントに渡された属性値をまとめてオブジェクト化されたもの
state
WEBアプリでは何かしらのユーザ操作やその他イベントによって、UIの一部を変更する必要があることが多いと思います。 例えば「ボタンを押したら、○○という文字列が△△に変わる」といった感じです。
言い換えると『コンポーネントの「状態○○」を「状態△△」に更新する』という状態管理をしていると言えます。
この状態管理で使う値をstate
といいます。
状態の更新がUIにすぐに反映されるのが特長のReact
では大事な要素の一つですね。
クラスコンポーネントでstate
を扱う場合はthis.state
という値を使って扱います。
state
もJavaScript
のオブジェクトになっていますが、
状態を更新する場合はこのオブジェクトを直接書き換えるのではなく
setState()
というメソッドを使用することになります。
これらのstate
の挙動については別途ブログでまとめようと思います。
[まとめ]
state
= コンポーネントの状態をまとめてオブジェクト化したもの。この値の操作でコンポーネントの状態を操作できる
プロパティ
頭の整理のためにプロパティについても言及しておきます。
JavaScript
という言語に限らず、クラスというものには「メソッド」と「プロパティ」が存在します。
ここまで見てきたprops
やstate
は、プログラムに落とし込むと少しプロパティに似たような感じに映ります。
しかし、props
やstate
はコンポーネント独自のものですが、プロパティはクラスであればコンポーネントに限らず持てるものです。
オブジェクトに値を保管しておく「オブジェクト変数」と呼ばれるもので、state
のような"操作可能なコンポーネントの状態"とは分けて考えるべきですね。
[まとめ]
プロパティ = クラスから生成したオブジェクトに持たせておく値。コンポーネントの状態とは関係がない
Hooks
「コンポーネント」の項で「関数コンポーネントはstate
を持たず、クラスコンポーネントはstate
を持つ」と書きました。
しかし、2018〜2019年頃にReact
に登場したHooks
という機能が付けられたことで、その概念は変わったそうです。
クラスを使わずとも関数コンポーネントにてstate
を使うことができるようになったからです。
状態管理を関数コンポーネントでも行えることになったことで、 クラスベースだったコンポーネント作成は関数ベースで作られることが増えたそうです。
その理由としては、クラスであるとコンポーネントの機能であるメソッドを書く場所が実装者によってバラバラに異なることがあります。 関数であるとその辛みが軽減されます。なぜなら、決まった機能は決まった箇所に書かれるからです。
実際にシンプルな例を書くと
const Parts = (props) => { const [name, setName] = useState(""); return ( <div> <h1>Hello {name}</h1> </div> ); }
ここにname
という状態(ステート)を定義しました。
ざっくりいうと、状態のGetterやSetter、そして初期値を設定しているようなイメージです。
なにやらクラスのようにも見えますが、これはあくまで関数ベースです。
Hooks
はこのような状態管理の機能を含め、色々な他の機能も有しています。
ここでは書ききれないので、また触ってみて動きを確かめたら別途ブログにしたいと思います。
Hooks
の扱い
さて、React
の公式はこの後発機能であるHooks
をどのようにしようと考えているかを確認してみます。
クラス型コンポーネントを削除する予定はない
ドキュメントに
クラスコンポーネントのユースケースをすべてフックがカバーできるようにする予定ではいますが、クラスコンポーネントのサポートも予見可能な将来にわたって続けていきます。Facebook では何万というコンポーネントがクラスとして書かれており、それらを書き換える予定は全くありません。代わりに、クラスと併用しながら新しいコードでフックを使っていく予定でいます。
と言及されているようにクラスベースのコンポーネントが使えなくなるということはないようですし、
もし古くからあるReact
のプロジェクト内のクラスベースのコンポーネントを関数コンポーネント+Hooks
に差し替えなくてはいけないということもなさそうです。
クラスコンポーネントを全部書き換える必要があるのですか?
いいえ。React からクラスを削除する予定はありません — 我々はみなプロダクトを世に出し続ける必要があり、クラスを書き換えている余裕はありません。新しいコードでフックを試すことをお勧めします。
採用するのはどちら?
同じようにドキュメントには
フック、クラスのいずれを使うべきですか、あるいはその両方でしょうか?
準備ができしだい、新しいコンポーネントでフックを試すことをお勧めします。チームの全員の準備が完了し、このドキュメントに馴染んでいることを確かめましょう。(例えばバグを直すなどの理由で)何にせよ書き換える予定の場合を除いては、既存のクラスをフックに書き換えることはお勧めしません。
クラスコンポーネントの定義内でフックを使うことはできませんが、クラス型コンポーネントとフックを使った関数コンポーネントとを 1 つのコンポーネントツリー内で混在させることは全く問題ありません。あるコンポーネントがクラスで書かれているかフックを用いた関数で書かれているかというのは、そのコンポーネントの実装の詳細です。長期的には、フックが React のコンポーネントを書く際の第一選択となることを期待しています。
これを見る限りはHooks
。つまり関数コンポーネントを推していきたいという考えのようです。
新たにReact
を始めるよという場合にはHooks
を使う前提で良いかもしれません。
もちろん、クラスを使う必要性が出てくればそれを採用することもアリだと、ドキュメントで謳われていますね。
[まとめ]
Hooks = 関数コンポーネントでシンプルかつリッチな機能を実現させるためのReact
の機構
ContextとProvider
Context
React
におけるContext
は、複雑になるコンポーネント間のデータ共有を解決するための仕組みです。
コンポーネントは細分化されて配置していくと親子関係になっていくと思いますが、
親コンポーネントにあるデータを子コンポーネントにprops
で渡して、さらにその子コンポーネントにprops
でデータを渡して・・・とやっていくと
非常に煩雑になることでしょう。
Context
はそういったデータのバケツリレーを避けるために、共通で(もしくは特定のコンポーネント群で)データをプールさせます。
const MenuContext = React.createContext(data); class MenuContainer extends React.Component { static contextType = MenuContext } class MenuItem extends React.Component { static contextType = MenuContext }
このように外でReact.createContext()
を使って共通データ用のContext
を作成し、
各コンポーネント内でcontextType
にセットしてやることで、
MenuContainer
でもMenuItem
でもContext
の値が使用できるようになります。
使用する際にはthis.context
から値を引き取ることができるという仕組みです。
props
以外で外からデータを渡せるという意味では覚えておきたいキーワードですね。
[まとめ]
Context = 複数のコンポーネント間で煩雑なデータのやりとりをしないように用意する共有データ。props
以外から外部からデータを取れる方法でもある
Provider
Context
を使うと、複数のコンポーネント間でひとつのデータを取り扱えるということでした。
しかし、そうなってくると同一コンポーネントであれば、常に同じContext
のデータを使用することになってしまい、
別の辛みが発生してしまいます。
そこでProvider
という仕組みを使います。Context
で渡される値を任意のコンポーネントオブジェクトだけ違う値を渡せるという機構です。
const MenuContext = React.createContext(data); class MenuItem extends React.Component { static contextType = MenuContext } // レンダリングの部分だけ抽出 return ( <div> <MenuItem /> <MenuItem /> <MenuContext.Provider value={anotherData}> <MenuItem /> </MenuContext.Provider> </div> );
この例でいうとMenuItem
コンポーネントが3つに並んでるのですが、
最後のひとつだけMenuContext.Provider
というタグに囲まれています。
こうすることで、3つ目のMenuItem
だけ data
ではなくanotherData
がcontext
から取得できるようになります。
この仕組みはテーマの分岐などでよく使われるとのことです。
[まとめ]
Provider = Context
のデータを一部だけ違うものに差し替えるための機構
Redux
React
のコンポーネントではそれぞれ個別に状態を管理しています。
それらを共通化できる仕組みとして前項ではContext
について書きましたが、
さらに複雑になってくるとそれでも追いつけなくなると思います。
そこで登場するのがRedux
になります。
Redux
は状態を統合して管理するユーティリティツールにあたります。
他にも同様のツールはあるそうですが、こちらがデファクトスタンダードのようですね。
React
とは別モノなので別途インストールが必要になるとのことです。
現時点ではまだRedux
は触れていないので、こちらもまた色々と検証してみてから別途ブログにまとめようと思います。
[まとめ]
Redux = アプリの値(状態)や処理を統合して管理できるReact
からは独立したユーティリティツール
まとめのまとめ
React
- React:
React
はFacebook社によって開発されたJavaScript
のためのユーザインターフェイス構築用のライブラリ- 宣言的ビュー
- コンポーネントベース
- Learn Once, Write Anywhere
DOM
- DOM:
JavaScript
からHTML
やXML
を操作するための「プログラミングインターフェイス」 - 仮想DOM:
JavaScript
でDOM操作を処理して描画コストを下げる仕組み - ノード:
DOM
の中を構成するモノひとつひとつのこと - エレメント: ノードの中でもHTML(またはXML)タグ自体のこと。「要素ノード」
JSX
- JSX:
HTML
ライクな記述ができるJavaScript
の言語拡張 - Babel:
JSX
を使用するために必要なコンパイラ
コンポーネント
- コンポーネント: 画面表示上の部品
- 関数コンポーネント:
state
を持たずレンダリング処理だけを持つ関数構文で作られたコンポーネント。 - クラス型コンポーネント:
React.Component
を継承したクラス記述でのコンポーネント
- 関数コンポーネント:
コンポーネント内の値
- props: コンポーネントに渡された属性値をまとめてオブジェクト化されたもの
- state: コンポーネントの状態をまとめてオブジェクト化したもの。この値の操作でコンポーネントの状態を操作できる
- プロパティ: クラスから生成したオブジェクトに持たせておく値。コンポーネントの状態とは関係がない
フック
- Hooks: 関数コンポーネントでシンプルかつリッチな機能を実現させるための
React
の機構
コンテキスト
- Context: 複数のコンポーネント間で煩雑なデータのやりとりをしないように用意する共有データ。
props
以外から外部からデータを取れる方法でもある - Provider:
Context
のデータを一部だけ違うものに差し替えるための機構
Redux
- Redux: アプリの値(状態)や処理を統合して管理できる
React
からは独立したユーティリティツール
最後に
ついつい長くなってしまいましたが、React
での開発で使いそうなキーワードをまとめてみました。
他にも必要なキーワードはまだまだありそう(例えばデザイン系とか)ですが、今回はこれくらいにしておこうと思います。
次回からは実際にReact
のプログラムを書いていくようなブログにしたいなぁと思っています。
では、またー。